/*
 * char_driver.c
 *
 * 本模块实现一个通用的字符设备驱动，支持：
 *  1. 从设备树（Device Tree）解析资源，包括 reg 和 interrupts、device-specific-data。
 *  2. 从 Platform Device（由 platform_driver.c 注册）获取资源，包括 resource、platform_data。
 *  3. 自动判断是否为 PCIe 设备：如果 platform_data 中 is_pcie = true 则认为是 PCIe 设备；
 *     如果从设备树获取到 "compatible" = "vendor,yourdev-pcie" 之类，则也可以判定。但这里以 platform_data 为准。
 *  4. 如果是 PCIe 设备，则启用 MSI（pci_enable_msi），并请求相应中断；否则按普通中断请求。
 *  5. 自动创建设备节点 (/dev/yourdevX)，并支持一次注册多组设备（由 platform_driver 中多个平台设备触发 probe）。
 *  6. 使用私有数据结构 struct yourdev_priv，将所有寄存器映射、IRQ、参数、状态等放进去，并使用 spinlock_t 
 *     保护对硬件的并发访问。
 *  7. 实现 read/write/fsync/unlocked_ioctl 等文件操作接口，并提供常规示例（读写寄存器、ioctl 读取/写寄存器、
 *     获取/设置设备状态等）。
 *  8. 提供 sysfs 属性，可以通过 /sys/class/yourdev/yourdevX/status 查看当前状态，通过 /sys/class/yourdev/yourdevX/param1 设置参数。
 *  9. 完整的 init/exit/错误回退逻辑，卸载时会 unregister character device、free_irq、iounmap、destroy class/device 等。
 *
 * 编译后生成 char_driver.ko。
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>

#include <linux/of_address.h>
#include <linux/of_irq.h>

#include "your_device.h"

#define DRIVER_NAME        "yourdev"    /* 用于匹配平台设备名称/设备树 compatible */
#define CLASS_NAME         "yourdev"    /* 用于创建 sysfs class：/sys/class/yourdev */

/* -----------------------------------------------
 * 1. 前向声明 & 全局变量
 * ----------------------------------------------- */
/* 主设备号动态分配 */
static dev_t yourdev_devt_base;
static unsigned int yourdev_major = 0; 

/* 保留一个 class，供所有设备共用 */
static struct class *yourdev_class;

/* Platform Driver 结构体 */
static int yourdev_probe(struct platform_device *pdev);
static int yourdev_remove(struct platform_device *pdev);

/* 匹配设备树 compatible 列表（若使用设备树描述） */
static const struct of_device_id yourdev_of_match[] = {
    { .compatible = "vendor,yourdev", },
    { .compatible = "vendor,yourdev-pcie", }, 
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, yourdev_of_match);

/* 匹配 Platform Device 名称列表（若平台直接注册设备） */
static const struct platform_device_id yourdev_id_table[] = {
    { "yourdev0", 0 },   /* 应与 platform_driver.c 中的 info->name 对应 */
    { "yourdev1", 0 },
    /* ... 根据需要添加更多设备名称 ... */
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, yourdev_id_table);

/* Platform Driver 结构体定义 */
static struct platform_driver yourdev_platform_driver = {
    .probe  = yourdev_probe,
    .remove = yourdev_remove,
    .driver = {
        .name           = DRIVER_NAME,
        .of_match_table = of_match_ptr(yourdev_of_match),
        /* 如果需要在 /sys/bus/platform 下查看 driver，可以再加 .owner = THIS_MODULE */
    },
    .id_table = yourdev_id_table,
};

/* ===============================================================
 * 2. 文件操作：open/close/read/write/ioctl
 * ===============================================================
 */
static int yourdev_open(struct inode *inode, struct file *filp)
{
    struct yourdev_priv *priv;

    /* 通过 inode->i_cdev 找到对应的 yourdev_priv */
    priv = container_of(inode->i_cdev, struct yourdev_priv, cdev);
    filp->private_data = priv;

    pr_info("yourdev: 打开设备 %s\n", dev_name(priv->device));
    return 0;
}

static int yourdev_release(struct inode *inode, struct file *filp)
{
    struct yourdev_priv *priv = filp->private_data;
    pr_info("yourdev: 释放设备 %s\n", dev_name(priv->device));
    return 0;
}

static ssize_t yourdev_read(struct file *filp, char __user *buf,
                            size_t count, loff_t *f_pos)
{
    struct yourdev_priv *priv = filp->private_data;
    uint32_t data;
    unsigned long flags;
    ssize_t ret = 0;

    /* 简单示例：读基地址 + 0x0 偏移的寄存器 */
    spin_lock_irqsave(&priv->lock, flags);
    data = readl(priv->base + 0x0);
    spin_unlock_irqrestore(&priv->lock, flags);

    /* 将 data（4 字节）拷贝到用户空间，最多拷贝 min(count, 4) */
    if (count < sizeof(data))
        return -EINVAL;

    if (copy_to_user(buf, &data, sizeof(data)))
        return -EFAULT;

    *f_pos += sizeof(data);
    ret = sizeof(data);
    pr_debug("yourdev: read 返回 0x%x\n", data);
    return ret;
}

static ssize_t yourdev_write(struct file *filp, const char __user *buf,
                             size_t count, loff_t *f_pos)
{
    struct yourdev_priv *priv = filp->private_data;
    uint32_t data;
    unsigned long flags;

    /* 简单示例：把用户传递的 4 字节写到寄存器偏移 0x0 */
    if (count < sizeof(data))
        return -EINVAL;

    if (copy_from_user(&data, buf, sizeof(data)))
        return -EFAULT;

    spin_lock_irqsave(&priv->lock, flags);
    writel(data, priv->base + 0x0);
    spin_unlock_irqrestore(&priv->lock, flags);

    *f_pos += sizeof(data);
    pr_debug("yourdev: write 写入 0x%x\n", data);
    return sizeof(data);
}

static long yourdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct yourdev_priv *priv = filp->private_data;
    struct yourdev_reg reg;
    struct yourdev_params params;
    int __user *p = (int __user *)arg;
    unsigned long flags;
    int ret = 0;

    /* 验证魔数 */
    if (_IOC_TYPE(cmd) != YOURDEV_IOC_MAGIC)
        return -ENOTTY;

    switch (cmd) {
    case YOURDEV_IOC_RESET:
        pr_info("yourdev: ioctl RESET\n");
        /* 假设 reset 就是把寄存器 0x0 写 0 */
        spin_lock_irqsave(&priv->lock, flags);
        writel(0, priv->base + 0x0);
        priv->status = 0;
        spin_unlock_irqrestore(&priv->lock, flags);
        break;

    case YOURDEV_IOC_READ_REG:
        /* 从用户空间取出 offset */
        if (copy_from_user(&reg, (void __user *)arg, sizeof(reg)))
            return -EFAULT;
        spin_lock_irqsave(&priv->lock, flags);
        reg.value = readl(priv->base + reg.offset);
        spin_unlock_irqrestore(&priv->lock, flags);
        /* 把读到的 value 拷贝回用户空间 */
        if (copy_to_user((void __user *)arg, &reg, sizeof(reg)))
            return -EFAULT;
        break;

    case YOURDEV_IOC_WRITE_REG:
        if (copy_from_user(&reg, (void __user *)arg, sizeof(reg)))
            return -EFAULT;
        spin_lock_irqsave(&priv->lock, flags);
        writel(reg.value, priv->base + reg.offset);
        spin_unlock_irqrestore(&priv->lock, flags);
        break;

    case YOURDEV_IOC_GET_STATUS:
        /* status 是一个 int，直接 copy_to_user */
        if (copy_to_user(p, &priv->status, sizeof(priv->status)))
            return -EFAULT;
        break;

    case YOURDEV_IOC_SET_PARAMS:
        if (copy_from_user(&params, (void __user *)arg, sizeof(params)))
            return -EFAULT;
        priv->params = params;
        pr_info("yourdev: ioctl SET_PARAMS param1=%d param2=%d\n",
                params.param1, params.param2);
        break;

    default:
        return -ENOTTY;
    }

    return ret;
}

static const struct file_operations yourdev_fops = {
    .owner          = THIS_MODULE,
    .open           = yourdev_open,
    .release        = yourdev_release,
    .read           = yourdev_read,
    .write          = yourdev_write,
    .unlocked_ioctl = yourdev_ioctl,
    /* 如果需要 poll/select，不在此示例提供 */
};

/* ===============================================================
 * 3. 中断处理程序示例
 * ===============================================================
 * 对于 PCIe 设备，如果支持 MSI，irq 会是 MSI 号；否则就是传统中断号。
 * 对于普通平台设备，irq 直接从 platform_resource 得到。
 */
static irqreturn_t yourdev_irq_handler(int irq, void *dev_id)
{
    struct yourdev_priv *priv = dev_id;
    /* 简单示例：读一个状态寄存器，并清中断 */
    uint32_t stat;

    stat = readl(priv->base + 0x4); /* 假设状态寄存器在偏移 0x4 */
    pr_info("yourdev: 中断触发，状态=0x%x\n", stat);

    /* 清中断（假设写 1 清除） */
    writel(1, priv->base + 0x4);

    /* 更新私有数据状态 */
    spin_lock(&priv->lock);
    priv->status = stat;
    spin_unlock(&priv->lock);

    return IRQ_HANDLED;
}

/* ===============================================================
 * 4. sysfs 属性：status、param1、param2 示例
 * ===============================================================
 * 通过 DEVICE_ATTR 宏定义
 */
static ssize_t status_show(struct device *dev,
                           struct device_attribute *attr, char *buf)
{
    struct yourdev_priv *priv = dev_get_drvdata(dev);
    return scnprintf(buf, PAGE_SIZE, "%u\n", priv->status);
}
static DEVICE_ATTR_RO(status);

static ssize_t param1_show(struct device *dev,
                           struct device_attribute *attr, char *buf)
{
    struct yourdev_priv *priv = dev_get_drvdata(dev);
    return scnprintf(buf, PAGE_SIZE, "%d\n", priv->params.param1);
}
static ssize_t param1_store(struct device *dev,
                            struct device_attribute *attr,
                            const char *buf, size_t count)
{
    struct yourdev_priv *priv = dev_get_drvdata(dev);
    int val;
    if (kstrtoint(buf, 10, &val) < 0)
        return -EINVAL;
    priv->params.param1 = val;
    return count;
}
static DEVICE_ATTR_RW(param1);

static ssize_t param2_show(struct device *dev,
                           struct device_attribute *attr, char *buf)
{
    struct yourdev_priv *priv = dev_get_drvdata(dev);
    return scnprintf(buf, PAGE_SIZE, "%d\n", priv->params.param2);
}
static ssize_t param2_store(struct device *dev,
                            struct device_attribute *attr,
                            const char *buf, size_t count)
{
    struct yourdev_priv *priv = dev_get_drvdata(dev);
    int val;
    if (kstrtoint(buf, 10, &val) < 0)
        return -EINVAL;
    priv->params.param2 = val;
    return count;
}
static DEVICE_ATTR_RW(param2);

/* ===============================================================
 * 5. probe() 函数 —— 资源获取 + 初始化
 * ===============================================================
 */
static int yourdev_probe(struct platform_device *pdev)
{
    struct resource *res_mem = NULL;
    struct resource *res_irq = NULL;
    struct yourdev_plat_info *platinfo = NULL;
    struct yourdev_priv *priv = NULL;
    struct device *dev = &pdev->dev;
    int ret = 0;
    bool is_pcie = false;
    struct pci_dev *pdev_pci = NULL;
    struct device_node *np = dev->of_node;

    pr_info("yourdev: probe(): 设备 %s\n", dev_name(dev));

    /* 分配私有数据结构 */
    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    spin_lock_init(&priv->lock);
    priv->pdev = NULL;  /* 默认为 NULL */
    priv->is_pcie = false;
    priv->status = 0;
    priv->params.param1 = 0;
    priv->params.param2 = 0;

    /* ===========================================================
     * 5.1 优先尝试通过设备树获取资源
     * ===========================================================
     * 如果设备树节点存在，且 reg/interrupts 描述有效，则按设备树方式获取。
     */
    if (np) {
        const char *compatible = NULL;
        // u32 irq_num;
        struct resource memres;

        compatible = of_get_property(np, "compatible", NULL);
        pr_info("yourdev: 使用设备树匹配: compatible=%s\n", compatible);

        /* 获取寄存器资源 */
        ret = of_address_to_resource(np, 0, &memres);
        if (ret) {
            pr_err("yourdev: 通过设备树获取寄存器资源失败 %d\n", ret);
            return ret;
        }
        res_mem = &memres;

        /* 获取中断资源 */
        ret = of_irq_to_resource(np, 0, &memres);
        if (ret) {
            pr_err("yourdev: 通过设备树获取中断资源失败 %d\n", ret);
            return ret;
        }
        res_irq = &memres;

        /* 判断是否为 PCIe 设备（假设 compatible 含 "-pcie" 即为 PCIe） */
        if (strstr(compatible, "-pcie")) {
            is_pcie = true;
            /* 需要通过 PCI 子系统获取 pci_dev */
            pdev_pci = pci_get_device(platinfo ? platinfo->vendor_id : 0,
                                      platinfo ? platinfo->device_id : 0, NULL);
            if (!pdev_pci) {
                pr_warn("yourdev: 设备树指定为 PCIe，但未找到对应 PCIe 设备\n");
                /* 仍然可以走普通模式，优先考虑 platform_data */
                is_pcie = false;
            } else {
                priv->pdev = pdev_pci;
                priv->is_pcie = true;
                /* 启用 MSI */
                ret = pci_enable_msi(pdev_pci);
                if (ret)
                    pr_warn("yourdev: 设备树 PCIe 设备不支持 MSI\n");
                /* 重新获取中断号 */
                res_irq->start = pdev_pci->irq;
                res_irq->end   = pdev_pci->irq;
            }
        }

        /* 保存物理地址长度 */
        priv->base = devm_ioremap_resource(dev, res_mem);
        if (IS_ERR(priv->base)) {
            pr_err("yourdev: ioremap 失败\n");
            return PTR_ERR(priv->base);
        }

        priv->irq = res_irq->start;

        /* 保存到 drvdata，以便 sysfs show/store 回调使用 */
        dev_set_drvdata(dev, priv);

        /* 请求中断 */
        ret = devm_request_irq(dev, priv->irq, yourdev_irq_handler,
                               0, dev_name(dev), priv);
        if (ret) {
            pr_err("yourdev: request_irq 失败 %d\n", ret);
            return ret;
        }
    } else {
        /* ===========================================================
         * 5.2 如果没有设备树节点，就通过 Platform Device 提供的资源
         * ===========================================================
         */
        /* 从 platform_device->resource 获取资源 */
        res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res_mem) {
            pr_err("yourdev: platform_get_resource(IORESOURCE_MEM) 失败\n");
            return -ENODEV;
        }
        res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (!res_irq) {
            pr_err("yourdev: platform_get_resource(IORESOURCE_IRQ) 失败\n");
            return -ENODEV;
        }

        /* 从 platform_data 获取 yourdev_plat_info 结构 */
        platinfo = pdev->dev.platform_data;
        if (!platinfo) {
            pr_err("yourdev: platform_data 为空\n");
            return -EINVAL;
        }

        is_pcie = platinfo->is_pcie;
        if (is_pcie) {
            /* 通过 vendor_id/device_id 获取 pci_dev */
            pdev_pci = pci_get_device(platinfo->vendor_id, platinfo->device_id, NULL);
            if (!pdev_pci) {
                pr_err("yourdev: 平台指定为 PCIe，但找不到 PCIe 设备 %04x:%04x\n",
                       platinfo->vendor_id, platinfo->device_id);
                return -ENODEV;
            }
            priv->pdev = pdev_pci;
            /* 启用 MSI */
            ret = pci_enable_msi(pdev_pci);
            if (ret)
                pr_warn("yourdev: PCIe 设备不支持 MSI，使用传统中断\n");
            /* 重新获取 BAR0 基地址 + offset */
            {
                resource_size_t bar0 = pci_resource_start(pdev_pci, 0);
                resource_size_t size = pci_resource_len(pdev_pci, 0);
                if (!bar0 || !size) {
                    pr_err("yourdev: PCIe BAR0 资源无效\n");
                    pci_disable_msi(pdev_pci);
                    pci_dev_put(pdev_pci);
                    return -EINVAL;
                }
                /* 加上 offset，再映射 */
                bar0 += platinfo->offset;
                size = platinfo->size ? platinfo->size : (size - platinfo->offset);
                priv->base = devm_ioremap(dev, bar0, size);
                if (!priv->base) {
                    pr_err("yourdev: PCIe ioremap 失败\n");
                    pci_disable_msi(pdev_pci);
                    pci_dev_put(pdev_pci);
                    return -ENOMEM;
                }
                priv->irq = pdev_pci->irq;
            }
        } else {
            /* 普通物理地址映射 */
            priv->base = devm_ioremap_resource(dev, res_mem);
            if (IS_ERR(priv->base)) {
                pr_err("yourdev: 普通 Platform 设备 ioremap 失败\n");
                return PTR_ERR(priv->base);
            }
            priv->irq = res_irq->start;
        }

        /* 保存到 drvdata */
        dev_set_drvdata(dev, priv);

        /* 请求中断 */
        ret = devm_request_irq(dev, priv->irq, yourdev_irq_handler,
                               0, dev_name(dev), priv);
        if (ret) {
            pr_err("yourdev: request_irq 失败 %d\n", ret);
            if (priv->is_pcie) {
                pci_disable_msi(priv->pdev);
                pci_dev_put(priv->pdev);
            }
            return ret;
        }
    }

    /* ===========================================================
     * 6. 注册字符设备：alloc_chrdev_region、cdev_init、cdev_add、class_create、device_create
     * ===========================================================
     */
    {
        int idx = pdev->id; /* platform_device_alloc 时指定的 id，即索引 */
        priv->devt = MKDEV(yourdev_major, idx);
        cdev_init(&priv->cdev, &yourdev_fops);
        priv->cdev.owner = THIS_MODULE;
        ret = cdev_add(&priv->cdev, priv->devt, 1);
        if (ret) {
            pr_err("yourdev: cdev_add 失败 %d\n", ret);
            goto err_pcie_cleanup;
        }

        /* 创建设备节点 */
        if (!yourdev_class) {
            pr_err("yourdev: yourdev_class 尚未创建\n");
            ret = -EINVAL;
            goto err_cdev_del;
        }
        priv->device = device_create(yourdev_class, dev, priv->devt,
                                     priv, "%s%d", DRIVER_NAME, idx);
        if (IS_ERR(priv->device)) {
            pr_err("yourdev: device_create 失败\n");
            ret = PTR_ERR(priv->device);
            goto err_cdev_del;
        }
    }

    /* ===========================================================
     * 7. 创建 sysfs 属性节点 status、param1、param2
     * ===========================================================
     */
    ret = device_create_file(priv->device, &dev_attr_status);
    if (ret) {
        pr_err("yourdev: sysfs 创建 status 属性失败\n");
        goto err_device_destroy;
    }
    ret = device_create_file(priv->device, &dev_attr_param1);
    if (ret) {
        pr_err("yourdev: sysfs 创建 param1 属性失败\n");
        goto err_remove_status;
    }
    ret = device_create_file(priv->device, &dev_attr_param2);
    if (ret) {
        pr_err("yourdev: sysfs 创建 param2 属性失败\n");
        goto err_remove_param1;
    }

    pr_info("yourdev: Probe 成功, [%s%d] base=0x%p irq=%d pcie=%d\n",
            DRIVER_NAME, pdev->id, priv->base, priv->irq, priv->is_pcie);

    return 0;

/* ===========================================================
 * 8. 错误回退分支
 * ===========================================================
 */
err_remove_param1:
    device_remove_file(priv->device, &dev_attr_param1);
err_remove_status:
    device_remove_file(priv->device, &dev_attr_status);
err_device_destroy:
    device_destroy(yourdev_class, priv->devt);
err_cdev_del:
    cdev_del(&priv->cdev);
err_pcie_cleanup:
    if (priv->is_pcie && priv->pdev) {
        pci_disable_msi(priv->pdev);
        pci_dev_put(priv->pdev);
    }
    return ret;
}

/* ===============================================================
 * 9. remove() 函数 —— 释放/注销
 * ===============================================================
 */
static int yourdev_remove(struct platform_device *pdev)
{
    struct yourdev_priv *priv = dev_get_drvdata(&pdev->dev);

    pr_info("yourdev: remove(): 设备 %s\n", dev_name(&pdev->dev));

    /* 删除 sysfs 属性 */
    device_remove_file(priv->device, &dev_attr_param2);
    device_remove_file(priv->device, &dev_attr_param1);
    device_remove_file(priv->device, &dev_attr_status);

    /* 注销字符设备 */
    device_destroy(yourdev_class, priv->devt);
    cdev_del(&priv->cdev);

    /* 关闭 PCIe MSI（如果启用过） */
    if (priv->is_pcie && priv->pdev) {
        pci_disable_msi(priv->pdev);
        pci_dev_put(priv->pdev);
    }

    /* 其余资源由 devm 自动释放（ioremap_resource, request_irq, kmalloc） */
    return 0;
}

/* ===============================================================
 * 10. 模块初始化 / 退出：注册 Platform Driver、创建设备号、class
 * ===============================================================
 */
static int __init yourdev_init(void)
{
    int ret;

    /* 1. 动态分配主设备号：最多支持 MAX_PLAT_DEVICES 个次设备号 */
    ret = alloc_chrdev_region(&yourdev_devt_base, 0,
                              MAX_PLAT_DEVICES, DRIVER_NAME);
    if (ret < 0) {
        pr_err("yourdev: alloc_chrdev_region 失败 %d\n", ret);
        return ret;
    }
    yourdev_major = MAJOR(yourdev_devt_base);

    /* 2. 创建设备类，供自动创建设备节点 */
    yourdev_class = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(yourdev_class)) {
        pr_err("yourdev: class_create 失败\n");
        ret = PTR_ERR(yourdev_class);
        goto err_unregister_chrdev;
    }

    /* 3. 注册 Platform Driver */
    ret = platform_driver_register(&yourdev_platform_driver);
    if (ret) {
        pr_err("yourdev: platform_driver_register 失败 %d\n", ret);
        goto err_class_destroy;
    }

    pr_info("yourdev: 字符设备驱动模块加载成功，主设备号=%d\n", yourdev_major);
    return 0;

err_class_destroy:
    class_destroy(yourdev_class);
err_unregister_chrdev:
    unregister_chrdev_region(yourdev_devt_base, MAX_PLAT_DEVICES);
    return ret;
}

static void __exit yourdev_exit(void)
{
    /* 注销 Platform Driver，会依次调用 remove() */
    platform_driver_unregister(&yourdev_platform_driver);

    /* 销毁 class */
    class_destroy(yourdev_class);

    /* 注销设备号 */
    unregister_chrdev_region(yourdev_devt_base, MAX_PLAT_DEVICES);

    pr_info("yourdev: 字符设备驱动模块卸载\n");
}

module_init(yourdev_init);
module_exit(yourdev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("示例作者");
MODULE_DESCRIPTION("一个支持设备树+平台设备+PCIe的通用字符设备驱动");
